use async_trait::async_trait;

use crate::commands::commands::ai::AiCommand;
use crate::commands::commands::base::BaseCommand;
use crate::commands::commands::calc::CalcCommand;
use crate::commands::commands::datetime::DatetimeCommand;
use crate::commands::commands::hmac::HmacCommand;
use crate::commands::commands::ip::IpCommand;
use crate::commands::commands::json::JsonCommand;
use crate::commands::commands::length::LengthCommand;
use crate::commands::commands::md5::Md5Command;
use crate::commands::commands::nanoid::NanoidCommand;
use crate::commands::commands::qrcode::QrCodeCommand;
use crate::commands::commands::rand_str::RandStrCommand;
use crate::commands::commands::search::SearchCommand;
use crate::commands::commands::sha::ShaCommand;
use crate::commands::commands::timestamp::TimestampCommand;
use crate::commands::commands::url::UrlCommand;
use crate::dao;
use crate::models::models;
use crate::models::models::command::CommandState;
use crate::models::models::commands::basic_command_setting::BasicCommandSetting;

pub(crate) mod timestamp;
pub(crate) mod length;
pub(crate) mod qrcode;
pub(crate) mod rand_str;
pub(crate) mod md5;
pub(crate) mod sha;
pub(crate) mod hmac;
pub(crate) mod base;
pub(crate) mod ai;
pub(crate) mod datetime;
pub(crate) mod calc;
pub(crate) mod json;
pub(crate) mod search;
pub(crate) mod ip;
pub(crate) mod url;
pub(crate) mod nanoid;

pub(crate) fn new_command(name: &str) -> Option<Box<dyn Command + Send>> {
    match name {
        "timestamp" => Some(Box::new(TimestampCommand::default()) as Box<dyn Command + Send>),
        "length" => Some(Box::new(LengthCommand::default()) as Box<dyn Command + Send>),
        "qrcode" => Some(Box::new(QrCodeCommand::default()) as Box<dyn Command + Send>),
        "randstr" => Some(Box::new(RandStrCommand::default()) as Box<dyn Command + Send>),
        "md5" => Some(Box::new(Md5Command::default()) as Box<dyn Command + Send>),
        "sha" => Some(Box::new(ShaCommand::default()) as Box<dyn Command + Send>),
        "hmac" => Some(Box::new(HmacCommand::default()) as Box<dyn Command + Send>),
        "base" => Some(Box::new(BaseCommand::default()) as Box<dyn Command + Send>),
        "ai" => Some(Box::new(AiCommand::default()) as Box<dyn Command + Send>),
        "datetime" => Some(Box::new(DatetimeCommand::default()) as Box<dyn Command + Send>),
        "calc" => Some(Box::new(CalcCommand::default()) as Box<dyn Command + Send>),
        "json" => Some(Box::new(JsonCommand::default()) as Box<dyn Command + Send>),
        "search" => Some(Box::new(SearchCommand::default()) as Box<dyn Command + Send>),
        "ip" => Some(Box::new(IpCommand::default()) as Box<dyn Command + Send>),
        "url" => Some(Box::new(UrlCommand::default()) as Box<dyn Command + Send>),
        "nanoid" => Some(Box::new(NanoidCommand::default()) as Box<dyn Command + Send>),
        _ => None
    }
}

/// 命令接口
#[async_trait]
pub(crate) trait Command: Send {
    /// 执行命令
    async fn execute(&self, command: models::command::Command) -> models::command::Command;

    /// 保存命令输出
    fn save_command_output(&self, mut command: models::command::Command, state: CommandState, output: String) -> models::command::Command {
        command.state = state;
        command.output = output.clone();

        let command_basic_setting = dao::setting::get_setting::<BasicCommandSetting>(format!("command.{}.config", command.name.as_str()).as_str());
        if command_basic_setting.is_none() || command_basic_setting.unwrap().append_to_history == true {
            let result = dao::command::update_command_history(&command);
            if result.is_err() {
                log::error!("更新历史命令失败: {}", result.unwrap_err().to_string());
            }
        }

        command
    }
}
